package org.barad.architecture.analyzer.archpad;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.LinkedHashSet;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.plaf.metal.MetalIconFactory;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeSelectionModel;

import org.barad.architecture.analyzer.archpad.cell.editor.JGraphUserObjectEditor;
import org.barad.architecture.analyzer.archpad.component.ComponentManager;
import org.barad.architecture.analyzer.archpad.event.handlers.JGraphTransferHandler;
import org.barad.architecture.analyzer.archpad.event.listeners.ArchDragAndDropMouseListener;
import org.barad.architecture.analyzer.archpad.event.listeners.JGraphContextMenuMouseListener;
import org.barad.architecture.analyzer.archpad.region.Region;
import org.barad.architecture.analyzer.archpad.region.RegionManager;
import org.barad.architecture.analyzer.archpad.util.Util;
import org.barad.architecture.analyzer.archpad.xml.handler.ComponentDescriptor;
import org.barad.architecture.analyzer.widgets.ArchPadButton;
import org.barad.architecture.analyzer.widgets.RegionJCheckBox;
import org.jgraph.graph.AbstractCellView;
import org.jgraph.graph.DefaultCellViewFactory;
import org.jgraph.graph.DefaultGraphModel;
import org.jgraph.graph.GraphLayoutCache;
import org.jgraph.graph.GraphModel;

import com.sun.corba.se.spi.orbutil.fsm.Action;

/**
 * This is the main class of the ArchPad application. It is
 * responsible for building the UI and adding event listeners
 * to that UI. Details about the design and implementation of
 * ArchPad can be found at: 
 * 
 * http://archpad.googlecode.com/files/Masters%20Thesis%20-%20Svetoslav%20Ganov.pdf
 * 
 * @author svetoslavganov@gmail.com (Svetoslav R. Ganov)
 */
public class ArchPad extends JFrame {
	public static final long serialVersionUID = 1L;
	private CustomizedJGraph customizedJGraph;
	private JMenuBar menuBar;
	private JMenu file;
	private JMenu model;
	private JMenuItem newModel;
	private JMenuItem openModel;
	private JMenuItem saveModel;
	private JMenuItem saveModelAs;
	private JMenuItem exit;
	private JMenuItem evaluateModel;
	private JMenuItem componentLibrary;
	private JToolBar mainToolBar;
	private JToolBar componentToolBar;
	private JButton newButton;
	private JButton openButton;
	private JButton saveButton;
	private JButton saveAsButton;
	private JButton saveCompositionButton;
	private JButton groupAsCompositionButton;
	private JButton ungroupCompositionButton;
	private JButton expandCompositionButton;
	private JButton collapseCompositionButton;
	private JButton createRegionButton;
	private JButton analyzeModelButton;
	private JTree componentHierarchyTree;
	private ArchPadButton linkButton;
	private JList componentList;
	private JTabbedPane tabbedPane;
	private JSplitPane splitPane;
	private LinkedHashSet<ArchPadButton> componentButtons;
	private List<ComponentDescriptor> archPadComponents;
	private JScrollPane scrollPane;
	private JPanel regionPanel;
	private static boolean linkDrawingEnabled;
	private Actions actions;
	private ComponentManager componentManager;
	private RegionManager regionManager;

	/**
	 * Creates an {@link ArchPad} instance.
	 */
	public ArchPad(){
		this.componentButtons = new LinkedHashSet<ArchPadButton>();
		this.componentManager = new ComponentManager(this);
		this.regionManager = new RegionManager(this);
		this.actions = new Actions(this);
		initSystem();
		initGUI();
		addListeners();
		setEnabledToolBarWidgets();
	}

	/*
	 * Initializes the system infrastructure such as logging,
	 * loading system properties, loading the model and so on.
	 */
	private void initSystem() {
		Util.loadLog4jProperties();
		Util.loadSystemPropeties();
		Util.loadModel();
		this.archPadComponents = Util.getComponentProperties();
	}

	/*
	 * Builds the GUI.
	 */
	private void initGUI() {
		initializeJFrame();
		createSplitPane();
		initJGraph();
		createMenuBar();	
		createFileMenu();
		createModelMenu();
		createComponentToolBar();
		createToolBarButtons();
		createTabbedPane();
		createComponentList();
		createComponentHierarchyTree();
		createRegionPanel();
		setVisible(true);
	}
	
	/*
	 * Initializes the JGraph component which stores the 
	 * software architecture model.
	 */
	private void initJGraph() {
		GraphModel model = new DefaultGraphModel();
		GraphLayoutCache view = new GraphLayoutCache(model, new DefaultCellViewFactory(), true);
		customizedJGraph = new CustomizedJGraph(this, model, view);
		customizedJGraph.setSize(2000, 2000);
		//allow dragging a cell into a group to add the cell to the group
		customizedJGraph.setMoveIntoGroups(true);
		//allow dragging a cell out of a group to remove the cell from the group
		customizedJGraph.setMoveOutOfGroups(true);
		AbstractCellView.cellEditor =  new JGraphUserObjectEditor();
		scrollPane = new JScrollPane(customizedJGraph);
		scrollPane.setMinimumSize(new Dimension(100, 100));
		splitPane.setLeftComponent(scrollPane); 
	}
	
	/*
	 * Creates a split pane i.e. splits the GUI into two parts.
	 * One part contains the JGraph component and the other 
	 * a tab panel with editors.
	 */
	private void createSplitPane() {
		splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation((getWidth() / 3) * 2);
		add(splitPane);
	}
	
	/*
	 * Initializes the main window of the application.
	 */
	private void initializeJFrame() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setTitle("ArchPad - ESEL ECE UT Austin");
		setSize(1024,768);
		Container cp = getContentPane();
		cp.add("North", mainToolBar = new JToolBar("Standard"));
	}
	
	/*
	 * Creates the menu bar.
	 */
	private void createMenuBar() {
		menuBar = new JMenuBar();
		setJMenuBar(menuBar);	
	}
	
	/*
	 * Creates the tool bar with the components of the model.
	 */
	private void createComponentToolBar() {
		componentToolBar = new JToolBar("Model");
		getContentPane().add(componentToolBar, "West");
		componentToolBar.setOrientation(JToolBar.VERTICAL);	
	}
	
	/*
	 * Creates the file menu. 
	 */
	private void createFileMenu() {
		file = new JMenu("File");
		menuBar.add(file);
		file.setMnemonic('F');
		//menu items
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		newModel = new JMenuItem("New Model", new ImageIcon(classLoader.getResource("new.gif")));
		newModel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
		file.add(newModel);
		openModel = new JMenuItem("Open Model", new ImageIcon(classLoader.getResource("open.gif")));
		openModel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
		file.add(openModel);
		saveModel = new JMenuItem("Save Model", new ImageIcon(classLoader.getResource("save.gif")));
		saveModel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
		saveModel.setMnemonic('s');
		file.add(saveModel);
		saveModelAs = new JMenuItem("Save Model As", new ImageIcon(classLoader.getResource("saveAs.gif")));
		saveModelAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK));
		file.add(saveModelAs);
		file.addSeparator();
		exit = new JMenuItem("Exit");
		exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));
		file.add(exit);
		file.addSeparator();
	}
	
	/*
	 * Creates the model menu.
	 */
	private void createModelMenu() {
		model = new JMenu("Model");
		menuBar.add(model);
		model.setMnemonic('M');
		evaluateModel = new JMenuItem("Evaluate");
		evaluateModel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));
		model.add(evaluateModel);
		componentLibrary = new JMenuItem("Component Library");
		componentLibrary.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.CTRL_MASK));
		model.add(componentLibrary);
	}
	
	/*
	 * Creates the buttons of the tool bar.
	 */
	private void createToolBarButtons() {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		newButton = new JButton(new ImageIcon(classLoader.getResource("new.gif")));
		newButton.setBorder(BorderFactory.createRaisedBevelBorder());
		newButton.setToolTipText("New Model");
		mainToolBar.add(newButton);
		openButton = new JButton(new ImageIcon(classLoader.getResource("open.gif")));
		openButton.setBorder(BorderFactory.createRaisedBevelBorder());
		openButton.setToolTipText("Open Model");
		mainToolBar.add(openButton);
		saveButton  = new JButton(new ImageIcon(classLoader.getResource("save.gif")));
		saveButton.setBorder(BorderFactory.createRaisedBevelBorder());
		saveButton.setToolTipText("Save Model");
		mainToolBar.add(saveButton);
		saveAsButton = new JButton(new ImageIcon(classLoader.getResource("saveAs.gif")));
		saveAsButton.setBorder(BorderFactory.createRaisedBevelBorder());
		saveAsButton.setToolTipText("Save Model As");
		mainToolBar.add(saveAsButton);
		mainToolBar.addSeparator();
		//composition manipulation buttons
		saveCompositionButton = new JButton(new ImageIcon(classLoader.getResource("saveAs.gif"))); 
		saveCompositionButton.setBorder(BorderFactory.createRaisedBevelBorder());
		saveCompositionButton.setToolTipText("Save composition");
		mainToolBar.add(saveCompositionButton);
		groupAsCompositionButton = new JButton(new ImageIcon(classLoader.getResource("group.gif"))); 
		groupAsCompositionButton.setBorder(BorderFactory.createRaisedBevelBorder());
		groupAsCompositionButton.setToolTipText("Group as composition");
		mainToolBar.add(groupAsCompositionButton);
		ungroupCompositionButton = new JButton(new ImageIcon(classLoader.getResource("ungroup.gif"))); 
		ungroupCompositionButton.setBorder(BorderFactory.createRaisedBevelBorder());
		ungroupCompositionButton.setToolTipText("Ugroup composition(s)");
		mainToolBar.add(ungroupCompositionButton);
		mainToolBar.addSeparator();
		//add component buttons
		expandCompositionButton = new JButton(new ImageIcon(classLoader.getResource("ZoomIn.gif")));
		expandCompositionButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 24));
		expandCompositionButton.setMaximumSize(new Dimension(30, 30));
		expandCompositionButton.setBorder(BorderFactory.createRaisedBevelBorder());
		expandCompositionButton.setToolTipText("Expand composition(s)");
		mainToolBar.add(expandCompositionButton);	
		collapseCompositionButton = new JButton(new ImageIcon(classLoader.getResource("ZoomOut.gif")));
		collapseCompositionButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 24));
		collapseCompositionButton.setMaximumSize(new Dimension(30, 30));
		collapseCompositionButton.setBorder(BorderFactory.createRaisedBevelBorder());
		collapseCompositionButton.setToolTipText("Colapse composition(s)");
		mainToolBar.add(collapseCompositionButton);
		mainToolBar.addSeparator();
		// region manipulation
		createRegionButton = new JButton(MetalIconFactory.getFileChooserListViewIcon());
		createRegionButton.setMaximumSize(new Dimension(30, 30));
		createRegionButton.setBorder(BorderFactory.createRaisedBevelBorder());
		createRegionButton.setToolTipText("Create region");
		mainToolBar.add(createRegionButton);
		mainToolBar.addSeparator();
		//analysis
		analyzeModelButton = new JButton(new ImageIcon(classLoader.getResource("Analyze.gif")));
		analyzeModelButton.setMaximumSize(new Dimension(30, 30));
		analyzeModelButton.setBorder(BorderFactory.createRaisedBevelBorder());
		analyzeModelButton.setToolTipText("Analyze model");
		mainToolBar.add(analyzeModelButton);
		mainToolBar.addSeparator();
		//add components buttons
		linkButton = new ArchPadButton("link.gif", false);
		linkButton.setIconFileName("link.gif");
		linkButton.setBorder(BorderFactory.createRaisedBevelBorder());
		linkButton.setToolTipText("Link");
		componentButtons.add(linkButton);
		componentToolBar.add(linkButton);
		for (ComponentDescriptor desc: archPadComponents) {
			ArchPadButton button = new ArchPadButton(desc, true);
			button.setToolTipText(desc.getToolTipText());
			button.setBorder(BorderFactory.createRaisedBevelBorder());
			componentButtons.add(button);
			componentToolBar.add(button);
		}
	}
	
	/**
	 * Enables the tool bar buttons that perform actions that are
	 * valid in the current context.
	 */
	public void setEnabledToolBarWidgets() {
		CustomizedJGraph jGraph = customizedJGraph;
		this.groupAsCompositionButton.setEnabled(jGraph.isGroupComponent());
		this.ungroupCompositionButton.setEnabled(jGraph.isUngroupComponent());
		this.saveCompositionButton.setEnabled(jGraph.isSaveComponent());
		this.expandCompositionButton.setEnabled(jGraph.isExpandComponent());
		this.collapseCompositionButton.setEnabled(jGraph.isCollapseComponent());
		this.createRegionButton.setEnabled(jGraph.isCreateRegion());
	}
	
	/**
	 * Builds the component hierarchy of the current selection in the 
	 * JGraph component i.e. the hierarchy of the selected components.
	 */
	public void buildComponentHierarchy() {
		componentManager.buildComponentHierarchy(componentHierarchyTree);
	}
	
	/**
	 * Clears the component hierarchy i.e. clears the currently displayed
	 * hierarchy in the corresponding tab of the tab panel.
	 */
	public void resetComponentHierarchy() {
		DefaultTreeModel model = (DefaultTreeModel)componentHierarchyTree.getModel();
		model.setRoot(null);
		componentHierarchyTree.validate();
	}
	
	/**
	 * Creates the tab pane that resides in the left hand side of the
	 * main window and contains tabs for different wizards.
	 */
	private void createTabbedPane() {
		tabbedPane = new JTabbedPane();
		JPanel panel1 = createInnerPanel("Library components");
		panel1.setToolTipText("Library components");
		tabbedPane.addTab("Components", panel1);
		JPanel panel2 = createInnerPanel("Component hierarchy");
		panel2.setToolTipText("Component hierarchy");
		tabbedPane.addTab("Hierarchy", panel2);
		JPanel panel3 = createInnerPanel("Architectural regions");
		panel3.setToolTipText("Architectural regions");
		tabbedPane.addTab("Regions", panel3);
		tabbedPane.setSelectedIndex(0);
		splitPane.add(tabbedPane);
	}
	
	/*
	 * Creates a list of the available library components. 
	 */
	private void createComponentList() {
		componentList = new JList(new DefaultListModel());
		componentList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		File file = new File(System.getProperty("user.dir"));
		actions.setComponentLibraryPath(file);
		actions.loadComponentLibary(componentList);
		JPanel panel = (JPanel)tabbedPane.getComponentAt(0);
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.fill = GridBagConstraints.BOTH;
		constraints.anchor = GridBagConstraints.FIRST_LINE_START;
		constraints.weightx = 1.0;
		constraints.weighty = 1.0;
		constraints.gridx = 0;
		GridBagLayout layout = new GridBagLayout();
		layout.setConstraints(componentList, constraints);
		JScrollPane scrollPane = new JScrollPane(componentList); 
		layout.setConstraints(scrollPane, constraints);
		panel.setLayout(layout);
		panel.add(scrollPane);
	}
	
	/*
	 * Creates the tree that represents the hierarchy of a component.
	 */
	private void createComponentHierarchyTree() {
		JPanel panel = (JPanel)tabbedPane.getComponentAt(1);
		DefaultTreeModel model = new DefaultTreeModel(null);
		componentHierarchyTree = new JTree(model);
		componentHierarchyTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		componentHierarchyTree.setEditable(false);
		componentHierarchyTree.validate();
		JScrollPane scrollPane = new JScrollPane(componentHierarchyTree);
		GridBagLayout layout = new GridBagLayout();
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.fill = GridBagConstraints.BOTH;
		constraints.anchor = GridBagConstraints.FIRST_LINE_START;
		constraints.weightx = 1.0;
		constraints.weighty = 1.0;
		constraints.gridx = 0;
		layout.setConstraints(scrollPane, constraints);
		panel.setLayout(layout);
		panel.add(scrollPane);
	}
	
	/*
	 * Creates the panel that provides region management functions.
	 */
	private void createRegionPanel() {
		regionPanel = (JPanel)tabbedPane.getComponentAt(2);
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.anchor = GridBagConstraints.FIRST_LINE_START;
		constraints.weighty = 1.0;
		GridBagLayout layout = new GridBagLayout();
		layout.setConstraints(regionPanel.getComponent(0), constraints);
		regionPanel.setLayout(layout);
	}
	
	/**
	 * Resets (clears) the region panel.
	 */
	public void resetRegionPanel() {
		Component panelTitle = regionPanel.getComponent(0);
		regionPanel.removeAll();
		regionPanel.add(panelTitle);
		regionPanel.repaint();
	}

	/**
	 * Creates an inner panel.
	 *  
	 * @param text The text for the label of the panel.
	 * @return The panel.
	 */
	private JPanel createInnerPanel(String text) {
		JPanel panel = new JPanel();
		JLabel display = new JLabel(text);
		display.setHorizontalAlignment(JLabel.CENTER);
		panel.add(display);
		return panel;
	}

	/*
	 * Adds the event listeners to the GUI components.
	 */
	private void addListeners() {
		// Menu item event listeners 
		
		// performs window closing
		addWindowListener(new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e){
				actions.exit();
			}
		});
		// performs creation of a new model
		newModel.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.newModel();
			}
		});
		// performs opening of a model
		openModel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae){
				actions.openModel();
			}
		});
		// performs saving of a model
		saveModel.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.saveModel();
			}
		});
		// performs saving a model as
		saveModelAs.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.saveModelAs();
			}
		});
		// performs exiting the application
		exit.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.exit();
			}
		});
		
		// Event listeners for tool bar buttons
		
		// performs creating a new model
		newButton.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.newModel();
			}
		});
		// performs opening of a model
		openButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae){
				actions.openModel();
			}
		});
		// performs saving a model
		saveButton.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.saveModel();
			}
		});
		// performs saving a model as
		saveAsButton.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.saveModelAs();
			}
		});
		// performs saving a component as
		saveCompositionButton.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				actions.saveComponentAs();
			}
		});
		// performs grouping of elements as a component
		groupAsCompositionButton.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				componentManager.groupAsComponent();
			}
		});
		// performs ungrouping of elements of a component
		ungroupCompositionButton.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				componentManager.ungroupSelectedComponents();
			}
		});
		// performs expanding a component
		expandCompositionButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				componentManager.expandComponent();
			}
		});
		// performs collapsing of a component
		collapseCompositionButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				componentManager.collapseComponent();
			}
		});
		// performs creating a region
		createRegionButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				regionManager.createRegion();
			}
		});
		// performs analysis a component
		analyzeModelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//TODO: Finish the new model and delegate to it
			}
		});
		// performs loading of the component library
		componentLibrary.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae){
				actions.setComponentLibraryPath();
				actions.loadComponentLibary(componentList);
			}
		});
		// performs handling of mouse events in the component list 
		ArchDragAndDropMouseListener archJListItemMouseListener = new ArchDragAndDropMouseListener();
		componentList.addMouseListener(archJListItemMouseListener);
		componentList.addMouseMotionListener(archJListItemMouseListener);
		// performs drawing of links between components
		linkButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae){
				Border border = BorderFactory.createEtchedBorder();
				if (linkButton.getBorder().equals(border)) {
					linkButton.setBorder(BorderFactory.createRaisedBevelBorder());
					linkDrawingEnabled = false;
				} else {
					linkButton.setBorder(border);
					linkDrawingEnabled = true;
				}
			}
		});
		// performs handling of property change in the split pane container.
		splitPane.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				if (evt.getPropertyName().equals("lastDividerLocation")) {
					int width = splitPane.getRightComponent().getWidth();
					int height = splitPane.getRightComponent().getHeight();
					JViewport viewPort = (JViewport)componentList.getParent();
					viewPort.getParent().setPreferredSize(new Dimension(width, height));
				}
			}
		});
		//add mouse listeners and transfer handlers to the component buttons
		for (ArchPadButton button: componentButtons) {
			button.setTransferHandler(new JGraphTransferHandler(this));
			ArchDragAndDropMouseListener listener = new ArchDragAndDropMouseListener();
			button.addMouseListener(listener);
			button.addMouseMotionListener(listener);
		}
		//add drop handler to JGraph
		customizedJGraph.setTransferHandler(new JGraphTransferHandler(this));
		//add mouse listener for showing context menu for JGraph
		JGraphContextMenuMouseListener listener = new JGraphContextMenuMouseListener(this);
		listener.setMouseListeners(customizedJGraph);
		customizedJGraph.addMouseListener(listener);
	}

	/**
	 * Starts the ArchPad application.
	 * 
	 * @param args An array of program arguments.
	 * NOTE: Currently the ArchPad tool does not take any arguments.
	 */
	public static void main(String[] args){
		new ArchPad();
	}

	/**
	 * Gets the {@link CustomizedJGraph} instance that contains the model.
	 * 
	 * @return The {@link CustomizedJGraph} instance.
	 */
	public CustomizedJGraph getCustomizedJGraph() {
		return customizedJGraph;
	}

	/**
	 * Gets if the link drawing is enabled.
	 * 
	 * @return True if link drawing is enabled, false otherwise.
	 */
	public boolean isLinkDrawingEnabled() {
		return linkDrawingEnabled;
	}

	/**
	 * Sets if the link drawing is enabled.
	 * 
	 * @param linkDrawingEnabled If the link drawing is enabled.
	 */
	public void setLinkDrawingEnabled(boolean linkDrawingEnabled) {
		ArchPad.linkDrawingEnabled = linkDrawingEnabled;
	}

	/**
	 * Gets the component list.
	 * 
	 * @return The component list.
	 */
	public JList getComponentList() {
		return componentList;
	}

	/**
	 * Sets the component list.
	 * 
	 * @param componentList The component list.
	 */
	public void setComponentList(JList componentList) {
		this.componentList = componentList;
	}

	/**
	 * Gets the region panel.
	 * 
	 * @return The region panel.
	 */
	public JPanel getRegionPanel() {
		return regionPanel;
	}
	
    /**
     * Gets the component manager.
     * 
     * @return The component manager.
     */
	public ComponentManager getComponentManager() {
		return componentManager;
	}

	/**
	 * Gets the {@link Action} instance.
	 * 
	 *  @return The {@link Action} instance.
	 */
	public Actions getActions() {
		return actions;
	}

	/**
	 * Gets the region manager.
	 * 
	 * @return the region manager.
	 */
	public RegionManager getRegionManager() {
		return regionManager;
	}
	
	/**
	 * Adds a check box for showing/hiding a region.
	 */
	public void addRegionJCheckBox() {
		JPanel regionPanel = getRegionPanel();
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.anchor = GridBagConstraints.FIRST_LINE_START;
		constraints.weightx = 1.0;
		constraints.weighty = 0.0;
		GridBagLayout layout = (GridBagLayout)regionPanel.getLayout();
		LinkedHashSet<Region> regions = getRegionManager().getRegions();
		Region lastRegion = (Region)regions.toArray()[regions.size() - 1];
		java.awt.Component lastComponent = regionPanel.getComponent(regionPanel.getComponentCount() - 1);
		JComponent jComponent = (JComponent)lastComponent;
		jComponent.setLayout(layout);
		layout.setConstraints(jComponent, constraints);
		JCheckBox checkBox = new RegionJCheckBox(this, lastRegion, lastRegion.getName());
		constraints.weighty = 1.0;
		layout.setConstraints(checkBox, constraints);
		regionPanel.add(checkBox);
		regionPanel.validate();
	}
}		